package com.atguigu.gulimall.cart.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.cart.feign.ProductFeignService;
import com.atguigu.gulimall.cart.interceptor.CartInterceptor;
import com.atguigu.gulimall.cart.service.CartService;
import com.atguigu.gulimall.cart.vo.Cart;
import com.atguigu.gulimall.cart.vo.CartItem;
import com.atguigu.gulimall.cart.vo.SkuInfoVo;
import com.atguigu.gulimall.cart.vo.UserInfoTo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private StringRedisTemplate redisTemplate;// 他一定要@Autowired,不然报错
    @Resource
    private ProductFeignService productFeignService;
    @Resource
    private ThreadPoolExecutor executor;
    // 购物车前缀
    private final String CART_PREFIX = "gulimall:cart:";

    @Override
    public CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {
        /**
         * 远程查询当前要添加的商品的信息和远程查询sku组合信息可以使用异步编排(多线程)
         * 好像只要是多个查询就可以使用异步编排来异步提速
         */

        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String result = (String) cartOps.get(skuId.toString());// 使用商品Id作为key去redis找这个数据

        if (StringUtils.isEmpty(result)) {
            /*购物车没有此商品*/
            // 1.远程查询当前要添加的商品的信息
            CartItem cartItem = new CartItem();// 添加新商品到购物车
            CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {
                R skuInfo = productFeignService.getSkuInfo(skuId);
                SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {
                });

                cartItem.setCheck(true);
                cartItem.setCount(num);
                cartItem.setImage(data.getSkuDefaultImg());
                cartItem.setTitle(data.getSkuTitle());
                cartItem.setSkuId(skuId);
                cartItem.setPrice(data.getPrice());
            }, executor);

            // 2.远程查询Sku的组合信息
            CompletableFuture<Void> getSkuSaleAttrValues = CompletableFuture.runAsync(() -> {
                List<String> values = productFeignService.getSkuSaleAttrValues(skuId);
                cartItem.setSkuAttr(values);
            }, executor);
            CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttrValues).get();
            /**
             * cart:1   1   {skuId:1,title:"华为","price:2999"}
             */
            String s = JSON.toJSONString(cartItem);
            cartOps.put(skuId.toString(), s);// 专门把购物车对象弄成json字符串,因为我们不想用redis自带的jdk序列化,太恶心了,什么也看不懂
            return cartItem;
        } else {
            /*购物车有此商品,修改数量*/
            // result举个例子:HASH:gulimall:cart:6 key:7 value:{"check":true,"count":3,"image":"https://xxxxxx","price":6299};
            CartItem cartItem = JSON.parseObject(result, CartItem.class);
            // 修改数量   原数量+加入购物车的数量=现在的数量
            cartItem.setCount(cartItem.getCount() + num);
            // 更新redis
            cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));
            return cartItem;
        }
    }

    /**
     * 获取购物车中某个购物项
     */
    @Override
    public CartItem getCartItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();
        String str = (String) cartOps.get(skuId.toString());
        CartItem cartItem = JSON.parseObject(str, CartItem.class);
        return cartItem;
    }

    /**
     * 获取整个购物车
     */
    @Override
    public Cart getCart() throws ExecutionException, InterruptedException {
        // 区分登录还是没登录状态,登录的话要合并购物车(就是把游客购物车里面的商品项也给加进来)
        Cart cart = new Cart();
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();// 得到拦截器里面放的用户信息
        if (userInfoTo.getUserId() != null) {
            // 1.说明登陆了
            String cartKey = CART_PREFIX + userInfoTo.getUserId();// 登录购物车
            String tempCartKey = CART_PREFIX + userInfoTo.getUserKey();// 临时(游客)购物车
            // 2.如果临时购物车的数据还没有进行合并
            List<CartItem> tempCartItems = getCartItems(tempCartKey);
            if (tempCartItems != null) {
                // 2.1说明临时购物车有数据,需要合并
                for (CartItem item : tempCartItems) {
                    addToCart(item.getSkuId(), item.getCount());//把临时购物车的数据添加到登录购物车里面,就是合并,两个参数分别是添加哪个商品,添加几件?
                }
                // 2.2合并完成后,清空临时(游客)购物车的数据
                clearCart(tempCartKey);
            }
            // 3.获取登录后的购物车数据[包含合并的临时(游客)购物车数据]
            List<CartItem> cartItems = getCartItems(cartKey);
            cart.setItems(cartItems);
        } else {
            // 没登录,用游客购物车
            String cartKey = CART_PREFIX + userInfoTo.getUserKey();// user-key就是游客购物车的cookie
            // 获取临时购物车的所有购物项
            List<CartItem> cartItems = getCartItems(cartKey);
            cart.setItems(cartItems);
        }
        return cart;
    }

    // 获取到我们要操作的购物车(操作哪个购物车)
    private BoundHashOperations<String, Object, Object> getCartOps() {
        UserInfoTo userInfoTo = CartInterceptor.threadLocal.get();
        String cartKey = "";
        if (userInfoTo.getUserId() != null) {
            // 说明人家登录了
            cartKey = CART_PREFIX + userInfoTo.getUserId();// 登录用户
        } else {
            // 否则用户没登录,就要用它临时购物车
            cartKey = CART_PREFIX + userInfoTo.getUserKey();// user-key是临时用户的cookie
        }

        BoundHashOperations<String, Object, Object> opertations = redisTemplate.boundHashOps(cartKey);
        return opertations;
    }

    // 获取购物车里面所有购物项
    private List<CartItem> getCartItems(String cartKey) {
        BoundHashOperations<String, Object, Object> hashOps = redisTemplate.boundHashOps(cartKey);
        List<Object> values = hashOps.values();
        if (values != null && values.size() > 0) {
            List<CartItem> collect = values.stream().map((obj) -> {
                String str = (String) obj;
                CartItem cartItem = JSON.parseObject(str, CartItem.class);
                return cartItem;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }

    // 清空临时(游客购物车数据)
    @Override
    public void clearCart(String cartkey) {
        redisTemplate.delete(cartkey);
    }
    // 勾选购物项
    @Override
    public void checkItem(Long skuId, Integer check) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();// 获取要操作的购物车
        CartItem cartItem = getCartItem(skuId);// 获取指定的购物项
        cartItem.setCheck(check==1?true:false);// 如果传过来的check==1那么他的实体属性值为true(被选中),否则为false(没有被选中)
        String s = JSON.toJSONString(cartItem);// 重新序列化存到redis里面
        cartOps.put(skuId.toString(),s);
    }
    // 改变购物项数量
    @Override
    public void changeItemCount(Long skuId, Integer num) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();// 获取要操作的购物车
        CartItem cartItem = getCartItem(skuId);// 获取指定的购物项
        cartItem.setCount(num);
        cartOps.put(skuId.toString(),JSON.toJSONString(cartItem));
    }

    @Override
    public void deleteItem(Long skuId) {
        BoundHashOperations<String, Object, Object> cartOps = getCartOps();// 获取要操作的购物车
        cartOps.delete(skuId.toString());
    }
}
